<template>
    <div class="bc-popover" ref="popover">
        <transition name="bc-popover-fade">
            <div v-if="init" v-show="show" class="bc-popover__content x-triangle" ref="content"
                :class="options.direction" :style="{top: top+'px', left: left+'px'}">
                <div class="bc-popover__box">
                    <slot name="content">请输入内容</slot>
                </div>
            </div>
        </transition>
        <slot />
    </div>
</template>

<script>
import { on, off } from '@/assets/js/utils.js'
import { getPopoverPosition } from '@/assets/js/vue-popper.js'
export default {
    name: 'BcPopover',
    props: {
        trigger: {
            type: String,
            default: 'hover',
            validator: value => ['click', 'hover'].includes(value)
        },
        placement: {
            type: String,
            default: 'right-middle',
            validator (value) {
                let dator = /^(top|bottom|left|right)(-start|-middle|-end)?$/g.test(value);
                return dator;
            }
        }
    },
    data() {
        return {
            init: false,
            show: false,
            left: 0,
            top: 0,
            options: {}
        }
    },
    watch: {
        init() {
            this.$nextTick(() => {
                let trigger = this.trigger,
                    content = this.$refs.content;
                // appendChild(已有元素)，是一个剪切的操作。
                document.body.appendChild(content);
                // 在鼠标移到content上时使content显示
                if (trigger === "hover") {
                    on(content, "mouseenter", this.handleMouseEnter);
                    on(content, "mouseleave", this.handleMouseLeave);
                }
            })
        },
        show() {
            if (this.show) {
                this.$nextTick(() => {
                    let { popover, content } = this.$refs,
                        { left, top, options } = getPopoverPosition(
                            popover,
                            content,
                            this.placement,
                            // 设置popver与content的间隙，不包括小三角形。
                            12
                        );
                    this.left = left;
                    this.top = top;
                    this.options = options;
                })
            }
        }
    },
    mounted() {
        let trigger = this.trigger, popover = this.$refs.popover;
        if (trigger === 'hover') {
            on(popover, "mouseenter", this.handleMouseEnter);
            on(popover, "mouseleave", this.handleMouseLeave);
        } else if (trigger === 'click') {
            on(popover, "click", this.handleClick);
        }
    },
    methods: {
        handleClick() {
            this.init = true;
            if (this.$refs.content && this.$refs.content.style.display === 'none') {
                this.$refs.content.style.display = 'block';
                this.show = true;
            } else {
                this.show = !this.show;
            }
            this.show && document.addEventListener('click', this.close);
        },
        close(e) {
            if (this.isPopover(e)) {
                this.show = false;
                document.removeEventListener('click', this.close);
            }
        },
        isPopover(e) {
            let dom = e.target,
                popover = this.$refs.popover,
                content = this.$refs.content;
            return !(popover.contains(dom) || content.contains(dom));
        },
        handleMouseEnter() {
            clearTimeout(this.time)
            this.init = true;
            this.show = true;
        },
        handleMouseLeave() {
            clearTimeout(this.time);
            // popover到content间有间隙，增加200ms延时，提升操作体验。
            this.time = setTimeout(() => {
                this.show = false;
            }, 200);
        }
    },
    beforeDestroy() {
        let { popover, content } = this.$refs;
        off(content, "mouseleave", this.handleMouseLeave);
        off(popover, "mouseleave", this.handleMouseLeave);
        off(content, "mouseenter", this.handleMouseEnter);
        off(popover, "mouseenter", this.handleMouseEnter);
        off(document, "click", this.close);
        document.body.removeChild(content);
    }
}
</script>

<style scoped>
.bc-popover {
    position: relative;
    display: inline-block;
}
.bc-popover .bc-popover__box {
    overflow: auto;
}
.bc-popover__content {
    position: absolute;
    color: #000;
    background-color: #fff;
    box-shadow: 0px 0px 3px rgb(172, 171, 171);
    padding: 6px;
    box-sizing: border-box; 
}
.bc-popover-fade-enter-active,
.bc-popover-fade-leave-active {
  transition: opacity .5s;
}
.bc-popover-fade-enter,
.bc-popover-fade-leave-to {
   opacity: 0;
}

.x-triangle::after {
    content: '';
    position: absolute;
    border: 6px solid white;
    border-top-color: transparent;
    border-left-color: transparent;
    border-right-color: transparent;
    border-bottom-color: transparent;
}
.x-triangle::before {
    content: '';
    position: absolute;
    border: 6px solid rgb(172, 171, 171);
    border-top-color: transparent;
    border-left-color: transparent;
    border-right-color: transparent;
    border-bottom-color: transparent;
}
.top-start::after {
    bottom: -11px;
    border-top-color: white;
    left: 5px;
}
.top-middle::after {
    bottom: -11px;
    border-top-color: white;
    left: calc(50% - 6px);
}
.top-end::after {
    bottom: -11px;
    border-top-color: white;
    right:5px;
}
.top-start::before {
    bottom: -12px;
    border-top-color: rgb(172, 171, 171);
    left: 5px;
}
.top-middle::before {
    bottom: -12px;
    border-top-color: rgb(172, 171, 171);
    left: calc(50% - 6px);
}
.top-end::before {
    bottom: -12px;
    border-top-color: rgb(172, 171, 171);
    right:5px;
}

.bottom-start::after {
    top: -11px;
    border-bottom-color: white;
    left: 5px;
}
.bottom-middle::after {
    top: -11px;
    border-bottom-color: white;
    left: calc(50% - 6px);
}
.bottom-end::after {
    top: -11px;
    border-bottom-color: white;
    right:5px;
}
.bottom-start::before {
    top: -12px;
    border-bottom-color: rgb(172, 171, 171);
    left: 5px;
}
.bottom-middle::before {
    top: -12px;
    border-bottom-color: rgb(172, 171, 171);
    left: calc(50% - 6px);
}
.bottom-end::before {
    top: -12px;
    border-bottom-color: rgb(172, 171, 171);
    right:5px;
}

.right-start::after {
    left: -11px;
    border-right-color:white;
    top: 5;
}
.right-middle::after { 
    left: -11px;
    border-right-color:white;
    top: calc(50% - 6px);
}
.right-end::after { 
    left: -11px;
    border-right-color:white;
    bottom: 5px;
}
.right-start::before {
    left:-12px;
    border-right-color: rgb(172, 171, 171);
    top: 5;
}
.right-middle::before {
    left:-12px;
    border-right-color: rgb(172, 171, 171);
    top: calc(50% - 6px);
}
.right-end::before {
    left:-12px;
    border-right-color: rgb(172, 171, 171);
    bottom: 5px;
}

.left-start::after {
    right: -11px;
    border-left-color:white;
    top: 5;
}
.left-middle::after { 
    right: -11px;
    border-left-color:white;
    top: calc(50% - 6px);
}
.left-end::after { 
    right: -11px;
    border-left-color:white;
    bottom: 5px;
}
.left-start::before {
    right: -12px;
    border-left-color: rgb(172, 171, 171);
    top: 5;
}
.left-middle::before {
    right: -12px;
    border-left-color: rgb(172, 171, 171);
    top: calc(50% - 6px);
}
.left-end::before {
    right: -12px;
    border-left-color: rgb(172, 171, 171);
    bottom: 5px;
}
</style>